package indi.zhang.securityrbac.utils;

import cn.hutool.core.convert.Convert;
import indi.zhang.securityrbac.annotation.DefaultValue;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 给对象赋默认值，非空处理
 */
public class DefaultValueUtils {

    /**
     * 反射字段判断类型，并且赋值默认值
     * @author 张洪源
     * @param javaObject
     * 0:使用方法注解判断是否赋指定认值
     *                 1:使用类注解判断是否赋默认值===>string->"",boolean->false,int->0,float->0f,double->0d,bigDecimal->BigDecimal.ZERO,long->0L
     *                 暂未实现自定义class类型的方法
     */
    public static void setNullDefault(Object javaObject) {
        try {
            if (isContainAnnotation(javaObject.getClass(),DefaultValue.class)) {
                methodAnnotation(javaObject);
            } else {
                classAnnotation(javaObject);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void classAnnotation(Object javaObject) throws Exception {
        Field[] fields = javaObject.getClass().getDeclaredFields();
        for (Field field : fields) {
            if ("serialVersionUID".equals(field.getName())) {
                continue;
            }
            // 如果使用了注解，获取注解的默认值并进行赋值
            if (isCustomType(field) && isFieldAnnotationPresent(field,DefaultValue.class)) {
                DefaultValue annotation = field.getAnnotation(DefaultValue.class);
                dealDefaultValue(javaObject,field,null);
            }
            dealNonBasicValue(javaObject,field);
        }
    }

    private static void methodAnnotation(Object javaObject) throws Exception {
        Field[] fields = javaObject.getClass().getDeclaredFields();
        for (Field field : fields) {
            if ("serialVersionUID".equals(field.getName())) {
                continue;
            }
            // 如果使用了注解，获取注解的默认值并进行赋值
            if (isFieldAnnotationPresent(field,DefaultValue.class)) {
                DefaultValue annotation = field.getAnnotation(DefaultValue.class);
                dealDefaultValue(javaObject,field,annotation.defaultValue());
            }
            dealNonBasicValue(javaObject,field);
        }
    }

    private static void dealDefaultValue(Object javaObject,Field field, String defaultValue)
            throws Exception {
        Class<?> type = field.getType();

        if (type.equals(int.class) || type.equals(Integer.class)) {
            field.setAccessible(true);
            if (field.get(javaObject) == null && defaultValue == null) {
                field.set(javaObject, 0);
                return ;
            }
            if (field.get(javaObject) == null) {
                field.set(javaObject, Convert.toInt(defaultValue));
            }
        }

        if (type.equals(float.class) || type.equals(Float.class)) {
            field.setAccessible(true);
            if (field.get(javaObject) == null && defaultValue == null) {
                field.set(javaObject, 0f);
                return ;
            }
            if (field.get(javaObject) == null) {
                field.set(javaObject, Convert.toFloat(defaultValue));
            }
        }

        if (type.equals(double.class) || type.equals(Double.class)) {
            field.setAccessible(true);
            if (field.get(javaObject) == null && defaultValue == null) {
                field.set(javaObject, 0d);
                return ;
            }
            if (field.get(javaObject) == null) {
                field.set(javaObject, Convert.toDouble(defaultValue));
            }
        }

        if (type.equals(long.class) || type.equals(Long.class)) {
            field.setAccessible(true);
            if (field.get(javaObject) == null && defaultValue == null) {
                field.set(javaObject, 0L);
                return ;
            }
            if (field.get(javaObject) == null) {
                field.set(javaObject, Convert.toLong(defaultValue));
            }
        }

        if (type.equals(boolean.class) || type.equals(Boolean.class)) {
            field.setAccessible(true);
            if (field.get(javaObject) == null && defaultValue == null) {
                field.set(javaObject, false);
                return ;
            }
            if (field.get(javaObject) == null) {
                field.set(javaObject, Convert.toBool(defaultValue));
            }
        }

        if (type.equals(String.class)) {
            field.setAccessible(true);
            if (field.get(javaObject) == null && defaultValue == null) {
                field.set(javaObject, "");
                return ;
            }
            if (field.get(javaObject) == null) {
                field.set(javaObject, defaultValue);
            }
        }

        if (type.equals(BigDecimal.class)) {
            field.setAccessible(true);
            if (field.get(javaObject) == null && defaultValue == null) {
                field.set(javaObject, BigDecimal.ZERO);
                return ;
            }
            if (field.get(javaObject) == null) {
                field.set(javaObject, Convert.toBigDecimal(defaultValue));
            }
        }
    }

    private static void dealNonBasicValue(Object javaObject,Field field) throws Exception {
        if (!isCustomType(field)) {
            setNullDefault(field.get(javaObject));
        }
        if (isListType(field)) {
            List<Object> objects = (List<Object>) field.get(javaObject);
            for (Object obj: objects) {
                setNullDefault(field.get(javaObject));
            }
        }
        if (isArrayType(field)) {
            Object[] objects = (Object[]) field.get(javaObject);
            for (Object obj: objects) {
                setNullDefault(field.get(javaObject));
            }
        }
    }

    /**
     * 判断字段是否包含目标注解
     * @param field
     * @param annotationClass
     * @return
     */
    public static boolean isFieldAnnotationPresent(Field field, Class<? extends Annotation> annotationClass) {
        if (field == null || annotationClass == null) {
            return false;
        }
        return field.isAnnotationPresent(annotationClass);
    }

    /**
     * 判断类是否包含目标注解
     * @param clazz
     * @param annotationType
     * @return
     */
    public static boolean isContainAnnotation(Class<?> clazz, Class<? extends Annotation> annotationType) {
        if (clazz == null || annotationType == null) {
            return false;
        }

        return clazz.isAnnotationPresent(annotationType);
    }

    /**
     * 判断类属性是否是常用准基本数据类型
     * @param field
     * @return
     */
    public static boolean isCustomType(Field field) {
        Class<?> clazz = field.getType();
        boolean isCustom = false;

        if (clazz.isArray()) {
            isCustom = true;
        } else if (Collection.class.isAssignableFrom(clazz)) {
            isCustom = true;
        } else if (Map.class.isAssignableFrom(clazz)) {
            isCustom = true;
        } else if (clazz == String.class || clazz == char.class) {
            isCustom = true;
        } else if (clazz == Integer.class || clazz == int.class) {
            isCustom = true;
        } else if (clazz == Float.class || clazz == float.class) {
            isCustom = true;
        } else if (clazz == Double.class || clazz == double.class) {
            isCustom = true;
        } else if (clazz == BigDecimal.class) {
            isCustom = true;
        } else if (clazz == Long.class || clazz == long.class) {
            isCustom = true;
        } else if (clazz == Boolean.class || clazz == boolean.class) {
            isCustom = true;
        } else if (clazz == Byte.class || clazz == byte.class) {
            isCustom = true;
        } else if (clazz == Short.class || clazz == short.class) {
            isCustom = true;
        }
        return isCustom;
    }

    /**
     * 判断类属性是否是list类型
     * @param field
     * @return
     */
    public static boolean isListType(Field field) {
        Class<?> clazz = field.getType();
        boolean isCustom = false;

        if (List.class.isAssignableFrom(clazz)) {
            isCustom = true;
        }
        return isCustom;
    }

    /**
     * 判断类属性是否是array类型
     * @param field
     * @return
     */
    public static boolean isArrayType(Field field) {
        Class<?> clazz = field.getType();
        boolean isCustom = false;

        if (clazz.isArray()) {
            isCustom = true;
        }
        return isCustom;
    }

}
